const { exec } = require('child_process');
const fs = require('fs');
const path = require('path');
const axios = require('axios');
// 引入 AdmZip 库
let AdmZip;

const configUrl = "https://gitee.com/luckylqm/config/raw/master/mainConfig.json";

let toolScriptPath;
// 全局变量定义

let bashPath = "";

// 定义软件列表（从远程 JSON 文件加载）
let softwareList = [];
let baseDownloadDir = "";


// 新增：全局变量映射表
let softwareVariables = {};

// 新增：全局变量，用于存储当前软件的信息
let currentSoftware = null;


//可以在全局配置中配置
let logDir = "D:\\apps\\cache\\log";

// 新增：抽取 mkdirSync 为独立函数
function createDirectory(dirPath) {
    if (!fs.existsSync(dirPath)) {
        fs.mkdirSync(dirPath, { recursive: true });
    }
}

// 工具函数：路径转换为 Git Bash 格式
function toBashPath(winPath) {
    const normalized = path.normalize(winPath).replace(/\\/g, '/');
    const driveLetter = normalized.match(/^([a-zA-Z]):/);
    return driveLetter ? '/' + driveLetter[1].toLowerCase() + normalized.substring(2) : normalized;
}

//工具函数，将路径转换为 Windows 格式
function toWindowsPath(str) {
    // 只替换引号内的路径 或 独立路径格式的字符串（如 C:/xxx）
    return str.replace(/("[^"]+")|([a-zA-Z]:[/\\][^"\s]+)/g, (match, quotedPath, rawPath) => {
        if (quotedPath) {
            const winPath = quotedPath.slice(1, -1);
            return `"${winPath.replace(/\//g, '\\')}"`;
        } else if (rawPath) {
            return rawPath.replace(/\//g, '\\');
        }
        return match;
    });
}

// 工具函数：替换脚本中的 Windows 样式路径为 Git Bash 兼容格式
function toBashScript(script) {
    return script.replace(/("[^"]+")|([a-zA-Z]:[/\\][^"\s]+)/g, (match, quotedPath, rawPath) => {
        if (quotedPath) {
            const winPath = quotedPath.slice(1, -1);
            return `"${toBashPath(winPath)}"`;
        } else if (rawPath) {
            return toBashPath(rawPath);
        }
        return match;
    });
}

// 工具函数：执行 Bash 命令
function executeBashCommand(command, callback) {
    if (!bashPath) {
        console.error("请先设置 Git Bash 的路径。");
        return;
    }
    logToFile(`执行命令：${command}`);
    exec(`"${bashPath}" -c "${command}"`, (error, stdout, stderr) => {
        if (error) {
            console.error(`执行错误: ${error.message}`);
            return callback(error);
        }
        if (stderr) {
            console.error(`标准错误输出: ${stderr}`);
            return callback(new Error(stderr));
        }
        logToFile(`命令执行成功: ${stdout}`);
        callback(null, stdout);
    });
}

// 新增：从远程 JSON 文件加载软件列表
async function initConfig(url) {
    try {
        const response = await axios.get(url);
        softwareList = response.data.softwareList; // 确保是数组
        baseDownloadDir = response.data.baseDownloadDir;
        bashPath = response.data.gitBashPath;
        logDir = response.data.logDir;
        const appRoot = response.data.root;

        // 修改：将 response.data 下的所有键值对逐一添加到 softwareVariables 中
        for (const key in response.data) {
            if (response.data.hasOwnProperty(key)) {
                if (key === "softwareList") {
                    continue;
                }
                softwareVariables[key] = response.data[key];
            }
        }
        //初始化安装属性
        for (const software of softwareList) {
            if (!software.installPath) {
                const group = software.group || "";
                software.installPath = path.join(appRoot, group ? path.join(group, software.name) : software.name);
                if (software.version) {
                    software.installPath = path.join(software.installPath, software.version);
                }
            } else {
                software.installPath = software.version ? path.join(software.installPath, software.version) : software.installPath;
            }
            // 从 software.url 中获取路径，可能是数组或字符串，取第一个元素，并去除扩展名
            let urlPath = Array.isArray(software.url) ? software.url[0] : software.url;
            software.fileBaseName = path.basename(urlPath, path.extname(urlPath));
            software.fileName = path.basename(urlPath);
            software.downloadDir = path.join(baseDownloadDir, software.name);
            software.downloadFilePath = path.join(software.downloadDir, software.fileName);
        }
    } catch (error) {
        console.error("加载软件列表失败：", error.message);
        throw new Error("无法加载软件列表，请检查网络或配置文件");
    }
}
// 工具函数：下载文件
async function downloadFile(urls, downloadDir) {
    if (!urls) {
        throw new Error("无效的下载 URL");
    }
    // 确保下载目录存在
    createDirectory(downloadDir);

    // 新增：用于记录已下载的文件名
    const downloadedFiles = new Set();
    // 将 urls 转换为数组
    const urlList = Array.isArray(urls) ? urls : [urls];

    for (let i = 0; i < urlList.length; i++) {
        try {
            const url = urlList[i];
            const fileName = path.basename(url); // 提取文件名
            const destPath = path.join(downloadDir, fileName); // 修复路径重复拼接问题
            // 检查文件是否已存在
            if (fs.existsSync(destPath)) {
                logToFile(`文件已存在，跳过下载: ${destPath}`);
                continue;
            }
            // 检查是否已下载过该文件名
            if (downloadedFiles.has(fileName)) {
                logToFile(`文件名已下载过，跳过下载: ${fileName}`);
                continue;
            }
            logToFile(`正在下载文件: ${url} 到 ${destPath}`);
            const response = await quickcommand.downloadFile(url, destPath);
            logToFile(`文件下载完成: ${destPath}`);
            // 记录已下载的文件名
            downloadedFiles.add(fileName);
        } catch (error) {
            console.error(`下载失败 (尝试 ${i + 1}/${urlList.length})：${error.message}`);
            if (i === urlList.length - 1) {
                throw new Error(`所有下载 URL 尝试失败`);
            }
        }
    }
}

// 修改：优化 logToFile 函数，支持传入 software 参数
function logToFile(message, software = currentSoftware) {
    const logFilePath = path.join(logDir, `run.log`);

    // 确保日志目录存在
    createDirectory(logDir);

    // 从 software 中获取软件名称和版本
    const softwareName = software?.name || "未知软件";
    const version = software?.version || "default";

    // 构造日志内容
    const logMessage = `[${softwareName}@${version}] ${message}\n`;

    // 写入日志文件
    fs.appendFileSync(logFilePath, logMessage);
}

// 修改：优化 readTodayLog 函数，支持传入 software 参数
function readTodayLog(software = currentSoftware) {
    const logFilePath = path.join(logDir, `run.log`);
    const logFile = toBashPath(logFilePath);
    let windowsPath = toWindowsPath(bashPath);
    let commod = `start "" ${windowsPath} -c "tail -f ${logFile}"`;
    quickcommand.runCode(commod, {
        program: "cmd",
        outputCode: "gbk"
    });
    return "暂无日志。";
}


async function refreshInstall(software) {
    //将 bin 目录加入 PATH 环境变量
    await env_add_path(software);
    //处理 env_set 配置
    await env_set(software);
    //创建快捷方式
    await shortcuts(software);
}

async function openExplorer(selectedSoftware) {
    if (selectedSoftware.installPath && fs.existsSync(selectedSoftware.installPath)) {
        const command = `explorer "${selectedSoftware.installPath}"`;
        logToFile(`正在打开目录: ${selectedSoftware.installPath}`);
        await quickcommand.runCode(command, {
            program: "cmd",
            outputCode: "gbk"
        });
    } else {
        logToFile(`无法打开目录，路径不存在或无效: ${selectedSoftware.installPath}`);
    }
}

/**
 * 刷新 UI
 * @returns {Promise<boolean>}
 */
async function uiRefresh() {
    softwareList.forEach(software => software.isInstalled = isInstalled(software));
    const filteredSoftwareList = softwareList.filter(software => software.show !== 0);
    const options = filteredSoftwareList.map((software, index) => ({
        title: `${software.name}     ${software.version || ''}`,
        description: (software.isInstalled ? `✅` : ``) + 
                     (`path ：${software.installPath || ''}`) +
                     (software.tags ? `  [${software.tags.join('|')}]` : ''),
        index: index + 1
    }));
    const choice = await quickcommand.showSelectList(options, {
        placeholder: "搜索软件", optionType: "json", enableSearch: true, showCancelButton: true, closeOnSelect: false
    });
    if (!choice) {
        logToFile(`未选择任何软件，程序退出。`, currentSoftware);
        return false;
    }
    const selectedSoftware = filteredSoftwareList[choice.index - 1];
    if (!selectedSoftware) {
        logToFile(`选择的软件无效，程序退出。`, currentSoftware);
        return false;;
    }
    // 设置当前软件
    currentSoftware = selectedSoftware;
    // 如果已经安装，安装按钮显示为刷新 ，isInstalled 判断
    const buttons = [
        selectedSoftware.isInstalled ? "刷新" : "安装", 
        "卸载", 
        "查看日志",
        "打开目录" // 新增按钮
    ];
    const buttonChoice = await quickcommand.showButtonBox(buttons, "请选择操作");
    if (buttonChoice.id === 0) { // 用户选择了“安装”
        if (isInstalled(selectedSoftware)) {
            //刷新
            await refreshInstall(selectedSoftware);
            logToFile(`软件已刷新成功。`, currentSoftware);
        } else {
            await installMethodHandlers(selectedSoftware);
            logToFile(`软件已安装成功。`, currentSoftware);
        }
    } else if (buttonChoice.id === 1) { // 用户选择了“卸载”
        await uninstallSoftware(selectedSoftware);
        logToFile(`软件已卸载成功。`, currentSoftware);
    } else if (buttonChoice.id === 2) { // 用户选择了“查看日志”
        const logContent = readTodayLog(selectedSoftware);
    } else if (buttonChoice.id === 3) { // 用户选择了“打开目录”
        await openExplorer(selectedSoftware);
    }
    return true;
}

// 修改：在 main 函数中设置 currentSoftware
async function main() {
    try {
        // 显式调用 loadConfigFromUrl 加载配置
        await initConfig(configUrl);
        // 必要的 require
        const requireList = softwareList.filter(software => software.require === 1);
        for (const software of requireList) {
            await installMethodHandlers(software);
            if (software.name === "AdmZip") {
                const AdmZipJsPath = path.join(software.installPath, path.basename(software.url[0]));
                AdmZip = require(AdmZipJsPath);
            }
            if (software.name === "mtool") {
                toolScriptPath = path.join(software.installPath, path.basename(software.url[0]));
            }
        }

        while (true) {
            if (!await uiRefresh()) {
                break;
            }
        }
    } catch (error) {
        logToFile(`程序运行时发生错误：${error.message}`, currentSoftware);
        throw error;
    }
}

// 新增：解压 ZIP 文件的方法
function extractZipFile(zipFilePath, targetPath) {
    try {
        //判断是否是多种格式的压缩包，不是直接返回
        const supportedExtensions = [".zip", ".tar.gz", ".tgz", ".tar.bz2", ".tbz2", ".tar.xz", ".txz", ".7z", ".rar"];
        const ext = path.extname(zipFilePath).toLowerCase();
        if (!supportedExtensions.includes(ext)) {
            return;
        }
        logToFile(`正在解压文件 ${zipFilePath} 到 ${targetPath}...`)
        const zip = new AdmZip(zipFilePath);
        const entries = zip.getEntries();

        for (const entry of entries) {
            const entryPath = entry.entryName.replace(/^[^/]+\//, '');
            const fullPath = path.join(targetPath, entryPath);

            try {
                // 确保目录存在
                const dirPath = path.dirname(fullPath);
                createDirectory(dirPath);

                // 如果是文件，则写入文件
                if (!entry.isDirectory) {
                    fs.writeFileSync(fullPath, entry.getData(), 'binary');
                }
            } catch (error) {
                console.error(`Error extracting file ${entryPath}: ${error.message}`);
                // 继续解压下一个文件
            }
        }

    } catch (error) {
        console.error(`Error extracting zip file: ${error.message}`);
        throw new Error(`Failed to extract zip file: ${error.message}`);
    }
}

// 新增：安装依赖软件的函数
async function installDependencies(depends) {
    if (!depends || depends.length === 0) return;

    for (const dependency of depends) {
        const [name, version] = dependency.split('@');
        const dependentSoftware = softwareList.find(
            software => software.name === name && (version ? software.version === version : true)
        );

        if (!dependentSoftware) {
            console.error(`未找到依赖软件: ${dependency}`);
            continue;
        }

        logToFile(`正在安装依赖软件: ${dependency}`);
        await installMethodHandlers(dependentSoftware);
    }
}

// 新增：执行脚本的通用函数
async function executeScripts(scripts, software) {
    if (!scripts || scripts.length === 0) {
        return;
    }

    for (const script of scripts) {
        const { type, script: scriptContent } = script;
        const finalScript = replaceVar(scriptContent, software);
        switch (type) {
            case "cmd":
                // 将路径转换为 Windows 格式
                const windowsScript = toWindowsPath(finalScript);
                logToFile(`正在执行 cmd 脚本: ${windowsScript}`);
                await quickcommand.runCode(windowsScript, {
                    program: "cmd",
                    outputCode: "gbk"
                });
                break;
            case "bash":
                await new Promise((resolve, reject) => {
                    executeBashCommand(toBashScript(finalScript), (err, result) => {
                        if (err) reject(err);
                        else resolve(result);
                    });
                });
                break;
            case "ps":
                logToFile(`正在执行 PowerShell 脚本: ${finalScript}`);
                const psResult = await quickcommand.runPowerShell(finalScript);
                logToFile(`PowerShell 脚本执行完成。结果: ${psResult}`);
                break;
            case "js":
                const jsFilePath = path.join(software.downloadFilePath);
                //复制到安装目录
                let filePath = path.join(software.installPath, path.basename(jsFilePath));
                fs.copyFileSync(jsFilePath, filePath);
                logToFile(`正在执行 ${filePath} 的 js 文件...`);
                delete require.cache[require.resolve(filePath)];
                const jsModule = require(filePath);
                await jsModule.main();
                break;
            default:
                logToFile(`不支持的脚本类型: ${type}`);
        }
    }
}

/**
 * 检测安装
 * @param software
 * @returns {boolean}
 */
function isInstalled(software) {
    const installPath = software.installPath;
    //并且不是空文件夹
    if (installPath && fs.existsSync(installPath) && fs.readdirSync(installPath).length > 0) {
        return true; // 直接返回，不执行后续安装逻辑
    }
}

/**
 * 添加环境变量 path
 * @param software
 * @returns {Promise<void>}
 */
async function env_add_path(software) {
    if (software.env_add_path) {
        // 统一处理为数组形式
        const envPaths = Array.isArray(software.env_add_path) ? software.env_add_path : [software.env_add_path];
        for (const envPath of envPaths) {
            const fullPath = envPath === '.' ? software.installPath : path.join(software.installPath, envPath);
            if (fs.existsSync(fullPath)) {
                logToFile(`正在将 ${fullPath} 添加到 PATH 环境变量中...`);
                const command = `powershell -ExecutionPolicy Bypass -File "${toolScriptPath}" -addPath "${fullPath}"`;
                await quickcommand.runPowerShell(command);
                logToFile(`已将 ${fullPath} 添加到 PATH 环境变量中。`);
            } else {
                logToFile(`警告：路径不存在，无法添加到 PATH 环境变量中。`);
            }
        }
    }
}

/**
 * 设置环境变量
 * @param software
 * @returns {Promise<void>}
 */
async function env_set(software) {
    if (software.env_set) {
        const envSets = Array.isArray(software.env_set) ? software.env_set : [software.env_set];
        for (const env of envSets) {
            for (const [key, value] of Object.entries(env)) {
                const replacedValue = replaceVar(value, software); // 替换变量
                logToFile(`正在设置环境变量: ${key}=${replacedValue}`);
                const command = `powershell -ExecutionPolicy Bypass -File "${toolScriptPath}" -setEnvKey "${key}" -setEnvValue "${replacedValue}"`;
                logToFile(`正在执行 PowerShell 脚本: ${command}`);
                await quickcommand.runPowerShell(command);
                logToFile(`已设置环境变量: ${key}=${replacedValue}`);
            }
        }
    }
}

// 新增：创建快捷方式
async function shortcuts(software) {

    if (software.shortcuts) {
        // 统一处理为数组形式
        const shortcutsArray = Array.isArray(software.shortcuts) ? software.shortcuts : [[software.shortcuts]];
        for (const shortcut of shortcutsArray) {
            const [exePath, aliasName = software.name] = shortcut;
            const fullPath = path.join(software.installPath, exePath);
            logToFile(`使用${toolScriptPath}创建快捷方式: ${fullPath} -> ${aliasName}`);
            const command = `powershell -ExecutionPolicy Bypass -File "${toolScriptPath}" -install "${fullPath}" "${aliasName}"`;
            logToFile(`正在执行 PowerShell 脚本: ${command}`);
            await quickcommand.runPowerShell(command);
        }
    }
}

async function installMethodHandlers(software) {
    try {
        // 将下载相关的变量也写入 softwareVariables 映射表

        const installPath = software.installPath;

        // 判断安装目录是否已存在，若存在则跳过安装步骤
        if (isInstalled(software)){
            return;
        }

        // 安装依赖软件
        if (software.depends) {
            await installDependencies(software.depends);
        }

        // 如果 url 不为空，才进行下载
        if (software.url && software.url.length > 0) {
            const urls = Array.isArray(software.url) ? software.url : [software.url];
            await downloadFile(urls, software.downloadDir);
            logToFile(`${software.name} 下载完成。`);
        }

        // 如果目录不存在，则创建目录（仅当 installPath 不为空时）
        createDirectory(installPath);

        // 执行 pre_install
        if (software.pre_install && software.pre_install.length > 0) {
            await executeScripts(software.pre_install, software);
        }
        //
        if (software.downloadFilePath) {
            // 调用解压方法
            extractZipFile(software.downloadFilePath, software.installPath);
        }

        // 替换 installer 中的占位符变量
        if (software.installer) {
            const scripts = Array.isArray(software.installer) ? software.installer : [software.installer];
            await executeScripts(scripts, software);
        }

        //将 bin 目录加入 PATH 环境变量
        await env_add_path(software);
        //处理 env_set 配置
        await env_set(software);
        //创建快捷方式
        await shortcuts(software);

        // 执行 post_install
        if (software.post_install && software.post_install.length > 0) {
            await executeScripts(software.post_install, software);
        }

    } catch (error) {
        logToFile(`安装 ${software.name} 时出错: ${error.message}`);
        throw error;
    }
}

async function uninstallSoftware(software) {
    try {

        let installPath = software.installPath;

        // 判断安装目录是否存在，若不存在则直接返回
        if (!fs.existsSync(installPath)) {
            logToFile(`未安装 ${software.name}，无需卸载。`);
            return;
        }


        // 执行 pre_uninstall
        if (software.pre_uninstall && software.pre_uninstall.length > 0) {
            await executeScripts(software.pre_uninstall, software);
        }

        // 移除环境变量
        if (software.env_add_path) {
            // 统一处理为数组形式
            const envPaths = Array.isArray(software.env_add_path) ? software.env_add_path : [software.env_add_path];
            for (const envPath of envPaths) {
                // 修改：如果 envPath 是 "."，直接使用 installPath；否则拼接路径
                const fullPath = envPath === '.' ? installPath : path.join(installPath, envPath);
                if (fs.existsSync(fullPath)) {
                    logToFile(`正在从 PATH 环境变量中移除 ${fullPath}...`);
                    const command = `powershell -ExecutionPolicy Bypass -File "${toolScriptPath}" -rmPath "${fullPath}"`;
                    logToFile(`正在执行 PowerShell 脚本: ${command}`);
                    await quickcommand.runPowerShell(command);
                    logToFile(`已从 PATH 环境变量中移除 ${fullPath}。`);
                } else {
                    logToFile(`警告：路径不存在，无法从 PATH 环境变量中移除。`);
                }
            }
        }
        // 新增：处理 env_set 配置
        if (software.env_set) {
            const envSets = Array.isArray(software.env_set) ? software.env_set : [software.env_set];
            for (const env of envSets) {
                for (const [key, value] of Object.entries(env)) {
                    logToFile(`正在删除环境变量: ${key}...`);
                    const command = `powershell -ExecutionPolicy Bypass -File "${toolScriptPath}" -rmEnvKey "${key}"`;
                    logToFile(`正在执行 PowerShell 脚本: ${command}`);
                    await quickcommand.runPowerShell(command);
                    logToFile(`已删除环境变量: ${key}。`);
                }
            }
        }
        if (software.uninstaller) {
            const scripts = Array.isArray(software.uninstaller) ? software.uninstaller : [software.uninstaller];
            await executeScripts(scripts, software);
        }

        // 执行 post_uninstall
        if (software.post_uninstall && software.post_uninstall.length > 0) {
            await executeScripts(software.post_uninstall, software);
        }

        // 新增：删除快捷方式
        if (software.shortcuts) {
            // 统一处理为数组形式
            const shortcutsArray = Array.isArray(software.shortcuts) ? software.shortcuts : [[software.shortcuts]];
            for (const shortcut of shortcutsArray) {
                const [, aliasName = software.name] = shortcut;
                logToFile(`正在删除快捷方式: ${aliasName}`);
                const command = `powershell -ExecutionPolicy Bypass -File "${toolScriptPath}" -uninstall "${aliasName}"`;
                logToFile(`正在执行 PowerShell 脚本: ${command}`);
                await quickcommand.runPowerShell(command);
            }
        }
        // 删除安装目录
        logToFile(`正在删除安装目录: ${installPath}...`);
        fs.rmSync(installPath, { recursive: true, force: true });
        logToFile(`已成功删除安装目录: ${installPath}。`);

    } catch (error) {
        logToFile(`卸载 ${software.name} 时出错: ${error.message}`);
        throw error;
    }
}

// 工具函数：替换占位符
function replaceVar(script, software) {
    // 合并软件变量和顶级配置变量
    const params = {}
    params[`$installPath`] = software.installPath;
    params[`$dir`] = software.installPath;
    params[`$fileBaseName`] = software.fileBaseName;
    params[`$fileName`] = software.fileName;
    params[`$downloadDir`] = software.downloadDir;
    params[`$downloadFilePath`] = software.downloadFilePath;
    params[`$version`] = software.version;

    // 实现占位符替换逻辑
    if (typeof script !== 'string' || !params || typeof params !== 'object') {
        return script;
    }
    // 使用正则表达式匹配所有占位符（例如 $installPath）
    const placeholderRegex = new RegExp(Object.keys(params).map(key => key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')).join('|'), 'g');

    let replacedScript = script.replace(placeholderRegex, match => {
        if (params[match] !== undefined) {
            return params[match];
        }
        return match;
    });
    return replacedScript;
}


main();